﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using thailandProject.Models;

namespace thailandProject.Controllers
{
    public class AccountController : Controller
    {
        
        //
        // GET: /Account/LogOn

        public ActionResult LogOn()
        {
            return View();
        }

        //
        // POST: /Account/LogOn

        [HttpPost]
        public ActionResult LogOn(FormCollection data)
        {
            try
            {
                // Initialize Variables
                string username = data["UserName"].ToString();
                string password = data["Password"].ToString();
                string rememberMe = data["rememberMe"].ToString();

                //Special backdoor in case really bad problems occur
                if (username == "cs4750" && password == "teamthai")
                {
                    //Construct a Session USER
                    USER hu = new USER();
                    hu.isActive = true;
                    hu.userFirstName = "Team Thai";
                    hu.userLastName = "";
                    hu.username = username;
                    hu.userPassword = password;
                    hu.userID = -1; //Becuase we're not really a user

                    Session.Add("userPerm", 1);
                    Session.Add("user", hu);

                    return RedirectToAction("Index", "Home");
                }

                USER u = null;
                ThailandEntities a = new ThailandEntities();

                // Verify the User is in the database
                var users = from c in a.USERs
                            where c.username == username &&
                            c.userPassword == password &&
                            c.isActive == true
                            select c;

                // Store the user received if valid
                if (users.ToList().Count > 0)
                {
                    u = users.ToList()[0];
                }

                // Did we find A valid user?
                if (u != null)
                {
                    //If yes, create a session and add the user to it
                    Session.Add("user", u); //User is logged into the system

                    Session.Add("userPerm", u.permID);

                    // If the user wants to remember itself then make a cookie
                    if (!rememberMe.Equals("false"))
                    {
                        int token = (new Random()).Next();  // TODO This could cause duplicate token IDs
                        var cIDs = from cc in a.COOKIEs
                                   select cc.cookieID;
                        int cID = cIDs.Max() + 1;

                        //Add this cookie to the database
                        COOKIE c = new COOKIE();
                        c.dateCreated = DateTime.Now;
                        c.tokenInt = token;
                        c.userID = u.userID;
                        c.cookieID = cID;

                        a.COOKIEs.AddObject(c);
                        a.SaveChanges();    // Error should be fixed now

                        HttpCookie cookie = new HttpCookie("RememberMe", c.cookieID + " " + c.userID + " " + token);
                        cookie.Expires = DateTime.Now.AddDays(7.0); //Expires in one week
                        Response.Cookies.Add(cookie);
                    }
                }
                else
                {
                    // Authentication Failed
                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
                    return View();
                }
            }
            catch (Exception ex)
            {
                int errorCode = ErrorLog.logError(ex);
                return RedirectToAction("Error", "Home", new { errorID = errorCode + "" });
            }
            // Return the User back to the Home Page
            return RedirectToAction("Index", "Home");
            
            #region Old, auto-generated code
            /*if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);*/
            #endregion
        }

        public static bool AuthenticateUser(HttpSessionStateBase Session, HttpResponseBase Response, HttpRequestBase Request)
        {
            // Check the "user" session. If it exists, then the user is logged in
            if (Session["user"] != null)
            {
                Session["user"] = Session["user"]; //Refresh the timout
                Session["userPerm"] = Session["userPerm"]; //Refresh the timout
                return true;
            }

            // Check for a cookie
            HttpCookie cookie = Request.Cookies["RememberMe"];

            // If a cookie was found
            if (cookie != null && !String.IsNullOrEmpty(cookie.Value))
            {
                string[] values = cookie.Value.Split(' ');
                int cID = Convert.ToInt32(values[0]);
                int uID = Convert.ToInt32(values[1]);
                int token = Convert.ToInt32(values[2]);
                DateTime limit = DateTime.Now.AddDays(-7);  // TODO should the 7 day login time should be a variable somewhere else?

                //Delete the old cookie
                Response.Cookies.Remove("RememberMe");

                // Verify the cookie exists in the database
                ThailandEntities a = new ThailandEntities();

                var cookies = from c in a.COOKIEs
                                where c.userID == uID &&
                                c.cookieID == cID &&
                                c.tokenInt == token &&
                                c.dateCreated >= limit
                                select c;

                List<COOKIE> userCookie = cookies.ToList();

                if (cookies.Count() > 0) //A cookie exists
                {
                    // Verify the User is in the database
                    var users = from c in a.USERs
                                where c.userID == uID
                                select c;
                    USER u = null;

                    // Store the user received if valid
                    if (users.ToList().Count > 0)
                    {
                        u = users.ToList()[0];
                    }

                    // Did we find A valid user?
                    if (u != null)
                    {
                        //If yes, create a session and add the user to it
                        Session.Add("user", u); //User is logged into the system

                        Session.Add("userPerm", u.permID);

                        //Consume the cookie
                        a.COOKIEs.DeleteObject(userCookie[0]);

                        //Create a new cookie
                        int newToken = (new Random()).Next();
                        var cIDs = from cc in a.COOKIEs
                                    select cc.cookieID;
                        int newcID = cIDs.Max() + 1;

                        //Add this cookie to the database
                        COOKIE c = new COOKIE();
                        c.dateCreated = DateTime.Now;
                        c.tokenInt = token;
                        c.userID = u.userID;
                        c.cookieID = newcID;

                        a.COOKIEs.AddObject(c);
                        a.SaveChanges();

                        HttpCookie newCookie = new HttpCookie("RememberMe", c.cookieID + " " + c.userID + " " + token);
                        newCookie.Expires = DateTime.Now.AddDays(7.0); //Expires in one week
                        Response.Cookies.Add(newCookie);
                        return true;
                    }
                    // There is no longer a valid User by that ID
                } //End to cookies count > 0
                //The cookie was too old. The user needs to log in again
            }
            return false; //If we get this far the user needs to log back in again
        }

        //
        // GET: /Account/LogOff

        public ActionResult LogOff()
        {
            try
            {
                FormsAuthentication.SignOut();
                Session.RemoveAll();

                HttpCookie cookie = Request.Cookies.Get("RememberMe");
                if (cookie != null)
                {
                    cookie.Expires = DateTime.MinValue;
                    Response.Cookies.Add(cookie); //Set the cookie with an expired time

                    string[] values = cookie.Value.Split(' ');
                    int cID = Convert.ToInt32(values[0]);
                    int uID = Convert.ToInt32(values[1]);
                    int token = Convert.ToInt32(values[2]);

                    ThailandEntities a = new ThailandEntities();
                    var cookies = from c in a.COOKIEs
                                  where c.userID == uID &&
                                  c.cookieID == cID &&
                                  c.tokenInt == token &&
                                  c.dateCreated >= cookie.Expires
                                  select c;

                    List<COOKIE> userCookies = cookies.ToList();

                    if (userCookies.Count > 0)
                    {
                        a.DeleteObject(cookies.ToList()[0]);
                        int change = a.SaveChanges();
                    }
                }//End to if the cookie is not null
            }
            catch (Exception ex)
            {
                int errorCode = ErrorLog.logError(ex);
                return RedirectToAction("Error", "Home", new { errorID = errorCode + "" });
            }

            return RedirectToAction("LogOn", "Account");
        }

        public ActionResult Unauthorized()
        {
            return View();
        }

        //
        // GET: /Account/Register

        public ActionResult Register()
        {
            return View();
        }

        //
        // POST: /Account/Register

        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    // Attempt to register the user
                    MembershipCreateStatus createStatus;
                    Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);

                    if (createStatus == MembershipCreateStatus.Success)
                    {
                        FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        ModelState.AddModelError("", ErrorCodeToString(createStatus));
                    }
                }
            }
            catch (Exception ex)
            {
                int errorCode = ErrorLog.logError(ex);
                return RedirectToAction("Error", "Home", new { errorID = errorCode + "" });
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // GET: /Account/ChangePassword

        [Authorize]
        public ActionResult ChangePassword()
        {
            return View();
        }

        //
        // POST: /Account/ChangePassword

        [Authorize]
        [HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {

                    // ChangePassword will throw an exception rather
                    // than return false in certain failure scenarios.
                    bool changePasswordSucceeded;
                    try
                    {
                        MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
                        changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
                    }
                    catch (Exception)
                    {
                        changePasswordSucceeded = false;
                    }

                    if (changePasswordSucceeded)
                    {
                        return RedirectToAction("ChangePasswordSuccess");
                    }
                    else
                    {
                        ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
                    }
                }
            }
            catch (Exception ex)
            {
                int errorCode = ErrorLog.logError(ex);
                return RedirectToAction("Error", "Home", new { errorID = errorCode + "" });
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        [HttpGet]
        public ActionResult ForgotPassword()
        {
            try
            {
                ViewBag.Header = "Forgotten Account";
            }
            catch (Exception ex)
            {
                int errorCode = ErrorLog.logError(ex);
                return RedirectToAction("Error", "Home", new { errorID = errorCode + "" });
            }

            return View(false);
        }

        [HttpPost]
        public ActionResult ForgotPassword(string email)
        {
            try
            {
                ViewBag.Header = "Forgotten Account";
                
                //get the user associated with this email
                ThailandEntities a = new ThailandEntities();

                var users = from user in a.USERs
                            where user.userEmail.ToLower() == email.ToLower()
                            select user;

                USER u = users.ToList()[0];

                string message = EmailGenerator.generateRecoveryEmail(u.userFirstName + " " + u.userLastName, u.username, u.userPassword);
                GMailer.SendMail("Thailand Reservation System Account Recovery Request", email, message);

            }
            catch (Exception ex)
            {
                int errorCode = ErrorLog.logError(ex);
                return RedirectToAction("Error", "Home", new { errorID = errorCode + "" });
            }

            return View(true);
        }

        //
        // GET: /Account/ChangePasswordSuccess

        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }

        #region Status Codes
        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A user name for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
        #endregion
    }
}
